package service

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"time"

	"server/internal/app/dto"
	"server/internal/app/model"
	"server/internal/app/repository"
	"server/internal/core/server"
	"server/internal/core/store"
	"server/pkg/utils"

	"gorm.io/gorm"
)

const (
	defaultTokenTTL = 24 * time.Hour
)

var (
	adminTokenKey = "admin:token:%s"
)

type AdminAuthService struct {
	adminRepo     *repository.SysAdminRepository
	adminRoleRepo *repository.SysAdminRoleRepository
	loginLogRepo  *repository.SysAdminLoginLogRepository
}

func NewAdminAuthService() *AdminAuthService {
	return &AdminAuthService{
		adminRepo:     repository.NewSysAdminRepository(),
		adminRoleRepo: repository.NewSysAdminRoleRepository(),
		loginLogRepo:  repository.NewSysAdminLoginLogRepository(),
	}
}
func (s *AdminAuthService) Register(ctx context.Context, req *dto.AdminRegisterRequest) error {
	if _, err := s.adminRepo.GetByUsername(ctx, req.Username); err == nil {
		return server.NewMessageError("用户名已存在")
	} else if !errors.Is(err, gorm.ErrRecordNotFound) {
		return err
	}

	if _, err := s.adminRepo.GetByEmail(ctx, req.Email); err == nil {
		return server.NewMessageError("邮箱已存在")
	} else if !errors.Is(err, gorm.ErrRecordNotFound) {
		return err
	}

	hash, err := utils.HashPassword(req.Password)
	if err != nil {
		return server.NewMessageError("密码加密失败")
	}

	admin := &model.SysAdmin{
		Username: req.Username,
		Password: hash,
		Email:    req.Email,
		Status:   model.StatusPending,
	}

	if err := s.adminRepo.Create(ctx, admin); err != nil {
		return err
	}
	return nil
}

func (s *AdminAuthService) Login(ctx context.Context, req *dto.AdminLoginRequest, meta *dto.AdminLoginMeta) (*dto.AdminTokenInfo, error) {
	admin, err := s.adminRepo.GetByUsername(ctx, req.Username)
	if err != nil {
		if errors.Is(err, gorm.ErrRecordNotFound) {
			return nil, server.NewMessageError("用户名或密码错误")
		}
		return nil, err
	}

	if err := utils.VerifyPassword(req.Password, admin.Password); err != nil {
		return nil, server.NewMessageError("用户名或密码错误")
	}

	if admin.Status != model.StatusEnabled {
		return nil, server.NewMessageError("状态未启用")
	}

	token, err := utils.GenerateToken()
	if err != nil {
		return nil, err
	}
	now := time.Now()
	expiredAt := now.Add(defaultTokenTTL)
	resp := &dto.AdminTokenInfo{
		AdminID:   admin.ID,
		Token:     token,
		ExpiredAt: expiredAt.UnixMilli(),
	}

	// 记录redis
	if err := store.Redis.SetNX(ctx, fmt.Sprintf(adminTokenKey, token), utils.Json(resp), defaultTokenTTL).Err(); err != nil {
		return nil, server.NewMessageError("登录失败")
	}

	if err := s.adminRepo.Update(ctx, admin.ID, map[string]any{
		"last_login_at": now,
	}); err != nil {
		return nil, err
	}

	if err := s.loginLogRepo.Create(ctx, &model.SysAdminLoginLog{
		AdminID:   admin.ID,
		Token:     token,
		ExpiresAt: expiredAt,
		UserAgent: meta.UserAgent,
		Referer:   meta.Referer,
		IP:        meta.IP,
	}); err != nil {
		return nil, err
	}
	return resp, nil
}

func (s *AdminAuthService) Logout(ctx context.Context, token string) error {
	if err := store.Redis.Del(ctx, fmt.Sprintf(adminTokenKey, token)).Err(); err != nil {
		return server.NewMessageError("退出登录失败")
	}
	return nil
}

// ParseToken 解析token
func (s *AdminAuthService) ParseToken(ctx context.Context, token string) (*dto.AdminTokenInfo, error) {
	var adminToken *dto.AdminTokenInfo
	data, _ := store.Redis.Get(ctx, fmt.Sprintf(adminTokenKey, token)).Result()
	if data == "" {
		return nil, server.UnauthorizedError
	}
	if err := json.Unmarshal([]byte(data), &adminToken); err != nil {
		return nil, server.UnauthorizedError
	}
	return adminToken, nil
}
